const APPALACHIAN_STATE_IDLE = 0;
const APPALACHIAN_STATE_LOGIN = 1;
const APPALACHIAN_STATE_ADD = 2;
const APPALACHIAN_STATE_VERIFIED = 3;
const APPALACHIAN_STATE_ACTIVE = 4;
const APPALACHIAN_STATE_WARNING = 5;
const APPALACHIAN_STATE_VERIFYING = 6;
const APPALACHIAN_STATE_CHECK = 7;
const APPALACHIAN_STATE_SUCCESS = 8;
const APPALACHIAN_STATE_FAILURE = 9;
const APPALACHIAN_STATE_ADD_YADIS = 10;

var APPALACHIAN_STATES = {
    "idle" : APPALACHIAN_STATE_IDLE,
    "login" : APPALACHIAN_STATE_LOGIN,
    "add" : APPALACHIAN_STATE_ADD,
    "addyadis" : APPALACHIAN_STATE_ADD_YADIS,
    "verified" : APPALACHIAN_STATE_VERIFIED,
    "active" : APPALACHIAN_STATE_ACTIVE,
    "warning" : APPALACHIAN_STATE_WARNING,
    "verifying" : APPALACHIAN_STATE_VERIFYING,
    "check" : APPALACHIAN_STATE_CHECK,
    "success" : APPALACHIAN_STATE_SUCCESS,
    "failure" : APPALACHIAN_STATE_FAILURE
};

const APPALACHIAN_ACTION_LEAVE = 0;
const APPALACHIAN_ACTION_CLICK = 1;
const APPALACHIAN_ACTION_DETECT_DOM = 2;
const APPALACHIAN_ACTION_DETECT_URL = 3;
const APPALACHIAN_ACTION_OBSERVED_REDIRECT = 4;
const APPALACHIAN_ACTION_VERIFY = 5;
const APPALACHIAN_ACTION_WARN = 6;
const APPALACHIAN_ACTION_STAY = 7;
const APPALACHIAN_ACTION_SUCCEED = 8;
const APPALACHIAN_ACTION_FAIL = 9;
const APPALACHIAN_ACTION_RECORD = 10;
const APPALACHIAN_ACTION_NOOP = 11;
const APPALACHIAN_ACTION_ABORT = 12;
const APPALACHIAN_ACTION_SAVE = 13;
const APPALACHIAN_ACTION_DETECT_YADIS = 14;

var APPALACHIAN_ACTIONS = {
    "leave" : APPALACHIAN_ACTION_LEAVE,
    "click" : APPALACHIAN_ACTION_CLICK,
    "detectdom" : APPALACHIAN_ACTION_DETECT_DOM,
    "detectyadis" : APPALACHIAN_ACTION_DETECT_YADIS,
    "detecturl" : APPALACHIAN_ACTION_DETECT_URL,
    "observedredirect" : APPALACHIAN_ACTION_OBSERVED_REDIRECT,
    "verify" : APPALACHIAN_ACTION_VERIFY,
    "warn" : APPALACHIAN_ACTION_WARN,
    "stay" : APPALACHIAN_ACTION_STAY,
    "succeed" : APPALACHIAN_ACTION_SUCCEED,
    "fail" : APPALACHIAN_ACTION_FAIL,
    "record" : APPALACHIAN_ACTION_RECORD,
    "noop" : APPALACHIAN_ACTION_NOOP,
    "abort" : APPALACHIAN_ACTION_ABORT,
    "save" : APPALACHIAN_ACTION_SAVE
};

var OpenIDUse = function(id, requestor, field, form, method, destination) {
    this.id = id;
    this.requestor = requestor;
    this.field = field;
    this.form = form;
    this.method = method;
    this.destination = destination;
};

var AppalachianFSM = function() {
    this._next = null;
    this._use = null;
};

AppalachianFSM.prototype.init = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.init");
    this.doIdle();
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.init");
}

AppalachianFSM.prototype.storeUse = function(id, requestor, field, form, method, destination) {
    this._use = new OpenIDUse(id, requestor, field, form, method, destination);
};

AppalachianFSM.prototype.deleteUse = function() {
    this._use = null;
}

AppalachianFSM.prototype.commitUse = function() {
    AppalachianDB.recordUse(this._use.id, this._use.requestor, this._use.field, this._use.form);
    this.deleteUse();
};

AppalachianFSM.prototype.getUsedOpenID = function() {
    return (this._use) ? this._use.id : null;
};

AppalachianFSM.prototype.getRequestor = function() {
    return (this._use) ? this._use.requestor : null;
};

AppalachianFSM.prototype.getCurrentState = function() {
    return document.getElementById("appalachian-status-icon").getAttribute("status");
};

AppalachianFSM.prototype.setCurrentState = function(state) {
    document.getElementById("appalachian-status-icon").setAttribute("status", state);
};

AppalachianFSM.prototype.setAction = function(cont) {
    this._next = cont;
}

AppalachianFSM.prototype.advance = function(action) {
    AppalachianDebug.trace("fsm.js", "AppalachianFSM.advance " + action);
    this._next(action);
};

// doSTATE contract: set the state, then set up a continuation
// that cases depending on the action passed in; any actions that
// fit belong in this continuation, not in the doSTATE body since
// doSTATE may be the next step from several different states.
// 
// You may pass on to the next state from doSTATE if there is only
// one possible exit and it occurs immediately.

AppalachianFSM.prototype.doActive = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doActive");
    this.setCurrentState("active");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["click"]:
            AppalachianListener.start(
                this._use.requestor,
                this._use.method,
                this._use.destination
            );
            this.doLogin();
            break;
        case APPALACHIAN_ACTIONS["noop"]:
        case APPALACHIAN_ACTIONS["abort"]:
        case APPALACHIAN_ACTIONS["leave"]:
        default:
            this.doIdle();
            break;
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doActive");
};

AppalachianFSM.prototype.doAddAvailable = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAddAvailable");
    this.setCurrentState("addavailable");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["click"]:
            this.doAdd();
            break;
        case APPALACHIAN_ACTIONS["noop"]:
        case APPALACHIAN_ACTIONS["abort"]:
        case APPALACHIAN_ACTIONS["leave"]:
        default:
            this.doIdle();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAddAvailable");
};

AppalachianFSM.prototype.doAddYadisAvailable = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAddYadisAvailable");
    this.setCurrentState("addavailable");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["click"]:
            this.doAddYadis();
            break;
        case APPALACHIAN_ACTIONS["noop"]:
        case APPALACHIAN_ACTIONS["abort"]:
        case APPALACHIAN_ACTIONS["leave"]:
        default:
            this.doIdle();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAddYadisAvailable");
};

AppalachianFSM.prototype.doAdd = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAdd");
    this.setCurrentState("add");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["abort"]:
            this.doAddAvailable();
            break;
        case APPALACHIAN_ACTIONS["save"]:
            this.doIdle();
            break;
        default:
            this.doIdle();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAdd");
};

AppalachianFSM.prototype.doAddYadis = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAddYadis");
    this.setCurrentState("add");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["abort"]:
            this.doAddYadisAvailable();
            break;
        case APPALACHIAN_ACTIONS["save"]:
            this.doIdle();
            break;
        default:
            this.doIdle();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAdd");
};

// make sure anything going to this state calls this.storeUse()
AppalachianFSM.prototype.doLogin = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doLogin");
    this.setCurrentState("login");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["leave"]:
            this.doLoginNext();
            break;
        case APPALACHIAN_ACTIONS["observedredirect"]:
            AppalachianListener.stop();
            this.doVerifying();
            break;
        default:
            this.doIdle();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doLogin");
};

AppalachianFSM.prototype.doLoginNext = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doLoginNext");
    this.setCurrentState("login");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["observedredirect"]:
            AppalachianListener.stop();
            this.doVerifying();
            break;
        case APPALACHIAN_ACTIONS["leave"]:
        default:
            this.doIdle();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doLoginNext");
};

AppalachianFSM.prototype.doVerifying = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doVerifying");
    this.setCurrentState("verifying");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["verify"]:
            // @@@ this really belongs in succeed, but
            // we aren't using the success checking code yet;
            // remove when properly in place
            this.commitUse();
            this.doVerified();
            break;
        case APPALACHIAN_ACTIONS["warn"]:
            this.doWarning();
            break;
        default:
            this.doVerifying();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doVerifying");
};

AppalachianFSM.prototype.doVerified = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doVerified");
    this.setCurrentState("verified");
    this.setAction(function(action) {
        // @@@ no checking done for now, rather complex to track it
        // this.doCheck();
        this.doIdle();
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doVerified");
};

AppalachianFSM.prototype.doWarning = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doWarning");
    this.setCurrentState("warning");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["leave"]:
            this.doIdle();
            break;
        case APPALACHIAN_ACTIONS["stay"]:
            this.doIgnoreWarning();
            break;
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doWarning");
};

AppalachianFSM.prototype.doIgnoreWarning = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doIgnoreWarning");
    this.setCurrentState("ignorewarning");  
    this.setAction(function(action) {
        this.doIdle();
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doIgnoreWarning");
};

// @@@ this does nothing; put the success/failure check in Appalachian
AppalachianFSM.prototype.doCheck = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doCheck");
    this.setCurrentState("check");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["succeed"]:
            this.commitUse();
            this.doSuccess();
            break;
        case APPALACHIAN_ACTIONS["fail"]:
            this.deleteUse();
            this.doFailure();
            break;
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doCheck");
};

AppalachianFSM.prototype.doSuccess = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doSuccess");
    this.setCurrentState("success");
    this.doIdle();
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doSuccess");
};

AppalachianFSM.prototype.doFailure = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doFailure");
    this.setCurrentState("failure");
    this.doIdle();
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doFailure");
};

AppalachianFSM.prototype.doIdle = function() {
    AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doIdle");
    if (AppalachianCommands.apiLogin) {
        AppalachianCommands.apiLogin();
        AppalachianCommands.apiLogin = false;
    }
    this.setCurrentState("idle");
    this.setAction(function(action) {
        switch(action) {
        case APPALACHIAN_ACTIONS["detectdom"]:
            this.doAddAvailable();
            break;
        case APPALACHIAN_ACTIONS["detectyadis"]:
            this.doAddYadisAvailable();
            break;
        case APPALACHIAN_ACTIONS["detecturl"]:
            this.doActive();
            break;
        case APPALACHIAN_ACTIONS["click"]:
            AppalachianListener.start(this._use.requestor, this._use.method, this._use.destination);
            this.doLogin();
            break;
        case APPALACHIAN_ACTIONS["noop"]:
        case APPALACHIAN_ACTIONS["abort"]:
        case APPALACHIAN_ACTIONS["leave"]:
        default:
            this.doIdle();
        }
    });
    AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doIdle");
};
